use alloc::{
    boxed::Box,
    collections::btree_map::BTreeMap,
    string::{String, ToString},
    sync::Arc,
    vec::Vec,
};
use spin::rwlock::RwLock;
use syscall::scheme::{FileInfo, KernelScheme, SchemeRef, SchemeTy};

use crate::{
    context::{get_current_process, process::Process},
    memory::MEMORY_SIZE,
};

use super::{mount_to, ROOT_SCHEME};

pub struct ProcScheme {
    schemes: BTreeMap<String, SchemeRef>,
    path: String,
}

impl ProcScheme {
    pub fn new() -> SchemeRef {
        let scheme = Arc::new(RwLock::new(Self {
            schemes: BTreeMap::new(),
            path: String::new(),
        }));
        scheme.write().schemes.insert(".".into(), scheme.clone());
        scheme
    }

    pub fn init() {
        let new_scheme = Self::new();
        mount_to(
            new_scheme.clone(),
            ROOT_SCHEME.lock().clone(),
            "proc".to_string(),
        );
    }
}

impl KernelScheme for ProcScheme {
    fn when_mounted(&mut self, path: String, father: Option<SchemeRef>) {
        self.path.clear();
        self.path.push_str(path.as_str());
        if let Some(father) = father {
            self.schemes.insert("..".into(), father.clone());
        }
    }

    fn when_umounted(&mut self) {
        for (name, node) in self.schemes.iter() {
            if name != "." && name != ".." {
                node.write().when_umounted();
            }
        }
    }

    fn mount(&mut self, node: SchemeRef, name: String) {
        self.schemes.insert(name, node);
    }

    fn get_path(&self) -> String {
        self.path.clone()
    }

    fn open(&self, name: String) -> Option<SchemeRef> {
        self.schemes.get(&name).cloned()
    }

    fn scheme_type(&self) -> SchemeTy {
        SchemeTy::Dir
    }

    fn list(&self) -> alloc::vec::Vec<FileInfo> {
        let mut vec = Vec::new();
        for (name, scheme) in self.schemes.iter() {
            vec.push(FileInfo::new(name.clone(), scheme.read().scheme_type()));
        }
        vec
    }
}

pub struct ProcFileScheme {
    path: String,
    process: Arc<RwLock<Box<Process>>>,
}

impl ProcFileScheme {
    pub fn new() -> SchemeRef {
        let scheme = Arc::new(RwLock::new(Self {
            path: String::new(),
            process: get_current_process(),
        }));
        scheme
    }
}

impl KernelScheme for ProcFileScheme {
    fn when_mounted(&mut self, path: String, _father: Option<SchemeRef>) {
        self.path.clear();
        self.path.push_str(path.as_str());
    }

    fn when_umounted(&mut self) {}

    fn get_path(&self) -> String {
        self.path.clone()
    }

    fn read_at(&self, _offset: usize, buf: &mut [u8]) -> usize {
        buf.copy_from_slice(&self.process.read().rw_buf[0..buf.len()]);
        buf.len()
    }

    fn write_at(&self, _offset: usize, buf: &[u8]) -> usize {
        self.process.write().rw_buf[0..buf.len()].copy_from_slice(buf);
        buf.len()
    }

    fn size(&self) -> usize {
        MEMORY_SIZE.clone()
    }

    fn scheme_type(&self) -> SchemeTy {
        SchemeTy::File
    }
}
